#pragma once 

#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>
#include <vector>

using namespace std;

class SelectSvr
{
    public:
        //构造(初始化)
        SelectSvr()
        {
            max_fd_ = -1;
            FD_ZERO(&readfds_);
        }

        //析构(销毁)
        ~SelectSvr()
        {}

        //添加文件描述符到对应的事件集合(参数为需要添加的文件描述符)
        void AddFd(int fd)
        {   
            //1. 添加文件描述符到对应的可读事件集合当中
            FD_SET(fd, &readfds_);

            //2.添加完之后，文件描述符的个数就会变化 
            //  需要更新最大的文件描述符数值
            if(fd > max_fd_)
            {
                max_fd_ = fd;
            }
        }

        //去除文件描述符在事件集合当中
        void DeletFd(int fd)
        {
            //1. 将文件描述符从可读事件集合当中去除掉
            FD_CLR(fd, &readfds_);

            //2.更新最大的文件描述符数值
            //  从后往前遍历
            for(int i = max_fd_; i >= 0; i--)
            {
                if(FD_ISSET(i, &readfds_))
                {
                    max_fd_ = i;
                    break;  //如果在，就直接跳出
                }    
            }
        }


        int Select(vector<int>* vec, struct timeval* tv = NULL)
        {
            fd_set tmp = readfds_; 
            int ret = select(max_fd_ + 1, &tmp, NULL, NULL, tv);
            if(ret < 0) //监控出错了了
            {
                perror("select");
                return -1; 
            }
            else if(ret == 0)
            {
                printf("select timeout!\n");
                return 0;
            }

            //执行到下面就是正常监控到了，有可能只有一个文件描述符就绪了，也有可能有多个文件描述符就绪了
            //如果至少有一个文件描述符就绪，但是哪一个文件描述符就绪了，还不知道，需要判断
            //用 FD_ISSET 判断
            for(int i = 0; i <= max_fd_; i++)
            {
                if(FD_ISSET(i, &tmp))
                {
                    vec->push_back(i);
                }
            }
            return ret; //返回文件描述符
        }
    private:
        //保存当前最大的文件描述符数值
        int max_fd_;
        //保存可读事件集合
        fd_set readfds_;
};
